/*!	 layer_rendering_task.cpp
**	 Layer_RenderingTask implementation
**
**	......... ... 2016 Ivan Mahonin
**
**	This package is free software; you can redistribute it and/or
**	modify it under the terms of the GNU General Public License as
**	published by the Free Software Foundation; either version 2 of
**	the License, or (at your option) any later version.
**
**	This package is distributed in the hope that it will be useful,
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
**	General Public License for more details.
**
*/

#ifdef USING_PCH
#	include "pch.h"
#else
#ifdef HAVE_CONFIG_H
#	include <config.h>
#endif

#include <synfig/general.h>
#include <synfig/localization.h>

#include "layer_rendering_task.h"

#include <synfig/context.h>
#include <synfig/rendering/software/surfacesw.h>
#include <synfig/rendering/software/task/tasksurfaceresamplesw.h>

#include <synfig/debug/debugsurface.h>

#endif

using namespace std;
using namespace etl;
using namespace synfig;

Layer_RenderingTask::Layer_RenderingTask() { }

Rect
Layer_RenderingTask::get_bounding_rect() const
{
    Rect rect = Rect::zero();

    for (rendering::Task::List::const_iterator i = tasks.begin(); i != tasks.end(); ++i)
        if (*i) {
            rect = rect.is_valid() ? rect | (*i)->get_bounds() : (*i)->get_bounds();
        }

    return rect;
}

Color
Layer_RenderingTask::get_color(Context /* context */, const Point &pos)const
{
    for (rendering::Task::List::const_iterator i = tasks.begin(); i != tasks.end(); ++i) {
        if (*i && (*i)->valid_target() && (*i)->target_surface.type_is<rendering::SurfaceSW>()) {
            RectInt src_target_rect = (*i)->get_target_rect();
            Vector src_lt = (*i)->get_source_rect_lt();
            Vector src_rb = (*i)->get_source_rect_rb();

            Matrix units_to_src_pixels;
            units_to_src_pixels.m00 = (src_target_rect.maxx - src_target_rect.minx) / (src_rb[0] - src_lt[0]);
            units_to_src_pixels.m11 = (src_target_rect.maxy - src_target_rect.miny) / (src_rb[1] - src_lt[1]);
            units_to_src_pixels.m20 = src_target_rect.minx - src_lt[0] * units_to_src_pixels.m00;
            units_to_src_pixels.m21 = src_target_rect.miny - src_lt[1] * units_to_src_pixels.m11;

            Vector p = units_to_src_pixels.get_transformed(pos);
            Rect src_target_rectf(src_target_rect.minx, src_target_rect.miny, src_target_rect.maxx, src_target_rect.maxy);

            if (src_target_rectf.is_inside(p))
                return rendering::SurfaceSW::Handle::cast_dynamic((*i)->target_surface)
                       ->get_surface().linear_sample(p[0], p[1]);
        }
    }

    return Color(0.0, 0.0, 0.0, 0.0);
}

bool
Layer_RenderingTask::accelerated_render(Context /* context */, Surface *surface, int /* quality */, const RendDesc &renddesc, ProgressCallback * /* cb */) const
{
    assert(surface);

    surface->set_wh(renddesc.get_w(), renddesc.get_h());
    surface->clear();

    RectInt dest_target_rect(0, 0, renddesc.get_w(), renddesc.get_h());
    Vector dest_lt = renddesc.get_tl();
    Vector dest_rb = renddesc.get_br();

    if (dest_target_rect.valid()
            && approximate_not_equal(dest_lt[0], dest_rb[0])
            && approximate_not_equal(dest_lt[1], dest_rb[1])) {
        Matrix units_to_dest_pixels;
        units_to_dest_pixels.m00 = (dest_target_rect.maxx - dest_target_rect.minx) / (dest_rb[0] - dest_lt[0]);
        units_to_dest_pixels.m11 = (dest_target_rect.maxy - dest_target_rect.miny) / (dest_rb[1] - dest_lt[1]);
        units_to_dest_pixels.m20 = dest_target_rect.minx - dest_lt[0] * units_to_dest_pixels.m00;
        units_to_dest_pixels.m21 = dest_target_rect.miny - dest_lt[1] * units_to_dest_pixels.m11;

        for (rendering::Task::List::const_reverse_iterator ri = tasks.rbegin(); ri != tasks.rend(); ++ri) {
            if (*ri && (*ri)->valid_target() && (*ri)->target_surface.type_is<rendering::SurfaceSW>()) {
                RectInt src_target_rect = (*ri)->get_target_rect();
                Vector src_lt = (*ri)->get_source_rect_lt();
                Vector src_rb = (*ri)->get_source_rect_rb();

                Matrix src_pixels_to_units;
                src_pixels_to_units.m00 = (src_rb[0] - src_lt[0]) / (src_target_rect.maxx - src_target_rect.minx);
                src_pixels_to_units.m11 = (src_rb[1] - src_lt[1]) / (src_target_rect.maxy - src_target_rect.miny);
                src_pixels_to_units.m20 = src_lt[0] - src_target_rect.minx * src_pixels_to_units.m00;
                src_pixels_to_units.m21 = src_lt[1] - src_target_rect.miny * src_pixels_to_units.m11;

                Matrix transformation = src_pixels_to_units * units_to_dest_pixels;

                rendering::TaskSurfaceResampleSW::resample(
                    *surface,
                    dest_target_rect,
                    rendering::SurfaceSW::Handle::cast_dynamic((*ri)->target_surface)->get_surface(),
                    src_target_rect,
                    transformation,
                    1.f,
                    Color::INTERPOLATION_LINEAR,
                    false,
                    false,
                    1.f,
                    Color::BLEND_COMPOSITE);
            }
        }
    }


    return true;
}